// ==UserScript==
// @name               ChatGPT Search
// @name:zh-CN         ChatGPT 搜索
// @name:zh-SG         ChatGPT 搜索
// @name:zh-TW         ChatGPT 搜索
// @name:zh-HK         ChatGPT 搜索
// @namespace          https://greasyfork.org/scripts/456077
// @version            0.8.0
// @author             Zheng Bang-Bo(https://github.com/zhengbangbo)
// @description        ChatGPT answers displayed in sidebar after search (Google, Bing, Baidu, DuckDuckGo and DeepL)
// @description:zh-CN  侧栏显示 ChatGPT 回答（Google、Bing、百度、DuckDuckGo和DeepL）
// @description:zh-SG  侧栏显示 ChatGPT 回答（Google、Bing、百度、DuckDuckGo和DeepL）
// @description:zh-TW  側欄顯示 ChatGPT 回答（Google、Bing、百度、DuckDuckGo和DeepL）
// @description:zh-HK  側欄顯示 ChatGPT 回答（Google、Bing、百度、DuckDuckGo和DeepL）
// @license            MIT
// @icon               https://github.com/zhengbangbo/oss/raw/main/logo/chat-gpt-userscript.png
// @resource logo https://my.cdn.com/logo.png
// @resource text https://my.cdn.com/some-text.txt
// @match              https://*.google.com/search*
// @match              https://*.google.ad/search*
// @match              https://*.google.ae/search*
// @match              https://*.google.com.af/search*
// @match              https://*.google.com.ag/search*
// @match              https://*.google.com.ai/search*
// @match              https://*.google.al/search*
// @match              https://*.google.am/search*
// @match              https://*.google.co.ao/search*
// @match              https://*.google.com.ar/search*
// @match              https://*.google.as/search*
// @match              https://*.google.at/search*
// @match              https://*.google.com.au/search*
// @match              https://*.google.az/search*
// @match              https://*.google.ba/search*
// @match              https://*.google.com.bd/search*
// @match              https://*.google.be/search*
// @match              https://*.google.bf/search*
// @match              https://*.google.bg/search*
// @match              https://*.google.com.bh/search*
// @match              https://*.google.bi/search*
// @match              https://*.google.bj/search*
// @match              https://*.google.com.bn/search*
// @match              https://*.google.com.bo/search*
// @match              https://*.google.com.br/search*
// @match              https://*.google.bs/search*
// @match              https://*.google.bt/search*
// @match              https://*.google.co.bw/search*
// @match              https://*.google.by/search*
// @match              https://*.google.com.bz/search*
// @require			   https://*.google.com.bz/search*
// @require			   https://*.google.com.bz/search*
// @match              https://*.google.ca/search*
// @match              https://*.google.cd/search*
// @match              https://*.google.cf/search*
// @match              https://*.google.cg/search*
// @match              https://*.google.ch/search*
// @match              https://*.google.ci/search*
// @match              https://*.google.co.ck/search*
// @match              https://*.google.cl/search*
// @match              https://*.google.cm/search*
// @match              https://*.google.cn/search*
// @match              https://*.google.com.co/search*
// @match              https://*.google.co.cr/search*
// @match              https://*.google.com.cu/search*
// @match              https://*.google.cv/search*
// @match              https://*.google.com.cy/search*
// @match              https://*.google.cz/search*
// @match              https://*.google.de/search*
// @match              https://*.google.dj/search*
// @match              https://*.google.dk/search*
// @match              https://*.google.dm/search*
// @match              https://*.google.com.do/search*
// @match              https://*.google.dz/search*
// @match              https://*.google.com.ec/search*
// @match              https://*.google.ee/search*
// @match              https://*.google.com.eg/search*
// @match              https://*.google.es/search*
// @match              https://*.google.com.et/search*
// @match              https://*.google.fi/search*
// @match              https://*.google.com.fj/search*
// @match              https://*.google.fm/search*
// @match              https://*.google.fr/search*
// @match              https://*.google.ga/search*
// @match              https://*.google.ge/search*
// @match              https://*.google.gg/search*
// @match              https://*.google.com.gh/search*
// @match              https://*.google.com.gi/search*
// @match              https://*.google.gl/search*
// @match              https://*.google.gm/search*
// @match              https://*.google.gr/search*
// @match              https://*.google.com.gt/search*
// @match              https://*.google.gy/search*
// @match              https://*.google.com.hk/search*
// @match              https://*.google.hn/search*
// @match              https://*.google.hr/search*
// @match              https://*.google.ht/search*
// @match              https://*.google.hu/search*
// @match              https://*.google.co.id/search*
// @match              https://*.google.ie/search*
// @match              https://*.google.co.il/search*
// @match              https://*.google.im/search*
// @match              https://*.google.co.in/search*
// @match              https://*.google.iq/search*
// @match              https://*.google.is/search*
// @match              https://*.google.it/search*
// @match              https://*.google.je/search*
// @match              https://*.google.com.jm/search*
// @match              https://*.google.jo/search*
// @match              https://*.google.co.jp/search*
// @match              https://*.google.co.ke/search*
// @match              https://*.google.com.kh/search*
// @match              https://*.google.ki/search*
// @match              https://*.google.kg/search*
// @match              https://*.google.co.kr/search*
// @match              https://*.google.com.kw/search*
// @match              https://*.google.kz/search*
// @match              https://*.google.la/search*
// @match              https://*.google.com.lb/search*
// @match              https://*.google.li/search*
// @match              https://*.google.lk/search*
// @match              https://*.google.co.ls/search*
// @match              https://*.google.lt/search*
// @match              https://*.google.lu/search*
// @match              https://*.google.lv/search*
// @match              https://*.google.com.ly/search*
// @match              https://*.google.co.ma/search*
// @match              https://*.google.md/search*
// @match              https://*.google.me/search*
// @match              https://*.google.mg/search*
// @match              https://*.google.mk/search*
// @match              https://*.google.ml/search*
// @match              https://*.google.com.mm/search*
// @match              https://*.google.mn/search*
// @match              https://*.google.ms/search*
// @match              https://*.google.com.mt/search*
// @match              https://*.google.mu/search*
// @match              https://*.google.mv/search*
// @match              https://*.google.mw/search*
// @match              https://*.google.com.mx/search*
// @match              https://*.google.com.my/search*
// @match              https://*.google.co.mz/search*
// @match              https://*.google.com.na/search*
// @match              https://*.google.com.ng/search*
// @match              https://*.google.com.ni/search*
// @match              https://*.google.ne/search*
// @match              https://*.google.nl/search*
// @match              https://*.google.no/search*
// @match              https://*.google.com.np/search*
// @match              https://*.google.nr/search*
// @match              https://*.google.nu/search*
// @match              https://*.google.co.nz/search*
// @match              https://*.google.com.om/search*
// @match              https://*.google.com.pa/search*
// @match              https://*.google.com.pe/search*
// @match              https://*.google.com.pg/search*
// @match              https://*.google.com.ph/search*
// @match              https://*.google.com.pk/search*
// @match              https://*.google.pl/search*
// @match              https://*.google.pn/search*
// @match              https://*.google.com.pr/search*
// @match              https://*.google.ps/search*
// @match              https://*.google.pt/search*
// @match              https://*.google.com.py/search*
// @match              https://*.google.com.qa/search*
// @match              https://*.google.ro/search*
// @match              https://*.google.ru/search*
// @match              https://*.google.rw/search*
// @match              https://*.google.com.sa/search*
// @match              https://*.google.com.sb/search*
// @match              https://*.google.sc/search*
// @match              https://*.google.se/search*
// @match              https://*.google.com.sg/search*
// @match              https://*.google.sh/search*
// @match              https://*.google.si/search*
// @match              https://*.google.sk/search*
// @match              https://*.google.com.sl/search*
// @match              https://*.google.sn/search*
// @match              https://*.google.so/search*
// @match              https://*.google.sm/search*
// @match              https://*.google.sr/search*
// @match              https://*.google.st/search*
// @match              https://*.google.com.sv/search*
// @match              https://*.google.td/search*
// @match              https://*.google.tg/search*
// @match              https://*.google.co.th/search*
// @match              https://*.google.com.tj/search*
// @match              https://*.google.tl/search*
// @match              https://*.google.tm/search*
// @match              https://*.google.tn/search*
// @match              https://*.google.to/search*
// @match              https://*.google.com.tr/search*
// @match              https://*.google.tt/search*
// @match              https://*.google.com.tw/search*
// @match              https://*.google.co.tz/search*
// @match              https://*.google.com.ua/search*
// @match              https://*.google.co.ug/search*
// @match              https://*.google.co.uk/search*
// @match              https://*.google.com.uy/search*
// @match              https://*.google.co.uz/search*
// @match              https://*.google.com.vc/search*
// @match              https://*.google.co.ve/search*
// @match              https://*.google.vg/search*
// @match              https://*.google.co.vi/search*
// @match              https://*.google.com.vn/search*
// @match              https://*.google.vu/search*
// @match              https://*.google.ws/search*
// @match              https://*.google.rs/search*
// @match              https://*.google.co.za/search*
// @match              https://*.google.co.zm/search*
// @match              https://*.google.co.zw/search*
// @match              https://*.google.cat/search*
// @match              https://www.bing.com/search*
// @match              https://cn.bing.com/search*
// @match              https://www.baidu.com/s*
// @match              https://duckduckgo.com/*
// @match              https://www.deepl.com/translator*
// @connect            chat.openai.com
// @grant              GM_addStyle
// @grant              GM_deleteValue
// @grant              GM_getValue
// @grant              GM_info
// @grant              GM_registerMenuCommand
// @grant              GM_setValue
// @grant              GM_unregisterMenuCommand
// @grant              GM_xmlhttpRequest
// ==/UserScript==

(e => {
	const t = document.createElement("style");
	t.dataset.source = "vite-plugin-monkey", t.innerText = e, document.head.appendChild(t)
})(
	".chat-gpt-container{max-width:369px;margin-bottom:30px;border-radius:8px;border:1px solid #dadce0;padding:15px;flex-basis:0;flex-grow:1;word-wrap:break-word;white-space:pre-wrap}.chat-gpt-container p{margin:0}.chat-gpt-container .prefix{font-weight:700}.chat-gpt-container .loading{color:#b6b8ba;animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.chat-gpt-container.sidebar-free{margin-left:60px;height:fit-content}.chat-gpt-container pre{white-space:pre-wrap;min-width:0;margin-bottom:0;line-height:20px}.chat-gpt-translate-button{border-radius:8px;border:1px solid #dadce0;padding:5px}.chat-gpt-translate-button:hover{color:#006494;transition:color .1s ease-out}.chat-gpt-translate-button[disabled]{color:#eee}");

(function() {
	"use strict";
	var monkeyWindow = window;
	var GM_info = /* @__PURE__ */ (() => monkeyWindow.GM_info)();
	var GM_setValue = /* @__PURE__ */ (() => monkeyWindow.GM_setValue)();
	var GM_deleteValue = /* @__PURE__ */ (() => monkeyWindow.GM_deleteValue)();
	var GM_addStyle = /* @__PURE__ */ (() => monkeyWindow.GM_addStyle)();
	var GM_registerMenuCommand = /* @__PURE__ */ (() => monkeyWindow.GM_registerMenuCommand)();
	var GM_unregisterMenuCommand = /* @__PURE__ */ (() => monkeyWindow.GM_unregisterMenuCommand)();
	var GM_xmlhttpRequest = /* @__PURE__ */ (() => monkeyWindow.GM_xmlhttpRequest)();
	var GM_getValue = /* @__PURE__ */ (() => monkeyWindow.GM_getValue)();

	function getUserscriptManager() {
		try {
			const userscriptManager = GM_info.scriptHandler;
			return userscriptManager;
		} catch (error) {
			return "other";
		}
	}

	function uuidv4() {
		var s = [];
		var hexDigits = "0123456789abcdef";
		for (var i = 0; i < 36; i++) {
			s[i] = hexDigits.substr(Math.floor(Math.random() * 16), 1);
		}
		s[14] = "4";
		s[19] = hexDigits.substr(s[19] & 3 | 8, 1);
		s[8] = s[13] = s[18] = s[23] = "-";
		var uuid = s.join("");
		return uuid;
	}
	const lang = navigator.appName == "Netscape" ? navigator.language : navigator.userLanguage;
	let config = {};
	switch (lang) {
		case "zh-CN":
		case "zh-SG":
			config = {
				waitingResponse: "等待 ChatGPT 响应...",
				login: "请在以下地址登录：",
				tooManyRequests: "1小时内请求太多。请稍后再试。",
				checkClouflare: "请通过 Cloudflare 安全检查，地址为",
				unknowError: "哦，可能是个错误，请检查或提交到",
				networkException: "网络异常，请刷新页面。",
				containerPosition: "容器位置 - 侧面(1)/顶部(0): ",
				chatGPTTranslate: "ChatGPT 翻译"
			};
			break;
		case "zh-TW":
		case "zh-HK":
			config = {
				waitingResponse: "等待 ChatGPT 回應...",
				login: "請在以下地址登錄：",
				tooManyRequests: "1小時內請求太多。請稍後再試。",
				checkClouflare: "請通過 Cloudflare 安全檢查，地址為",
				unknowError: "哦，可能是個錯誤，請檢查或提交到",
				networkException: "網路異常，請刷新頁面。",
				containerPosition: "容器位置 - 側面(1)/頂部(0):",
				chatGPTTranslate: "ChatGPT 翻譯"
			};
			break;
		default:
			config = {
				waitingResponse: "Waiting for ChatGPT response...",
				login: "Please login at ",
				tooManyRequests: "Too many requests in 1 hour. Try again later.",
				checkClouflare: "Please pass Cloudflare security check at ",
				unknowError: "Oops, maybe it is a bug, please check or submit ",
				networkException: "Network exception, please refresh the page.",
				containerPosition: "Container Position - Side(1)/Top(0): ",
				chatGPTTranslate: "ChatGPT Translate"
			};
	}

	function i18n(name, param) {
		return config[name] ? config[name].replace("#t#", param) : name;
	}
	const container = document.createElement("div");

	function getContainer() {
		return container;
	}

	function initContainer() {
		const container2 = getContainer();
		container2.className = "chat-gpt-container";
		container2.innerHTML = `<p class="loading">${i18n("waitingResponse")}</p>`;
	}

	function containerShow(answer) {
		const container2 = getContainer();
		container2.innerHTML = '<p><span class="prefix">ChatGPT</span><pre></pre></p>';
		container2.querySelector("pre").textContent = answer;
	}

	function containerAlert(htmlStr) {
		const container2 = getContainer();
		container2.innerHTML = htmlStr;
	}

	function alertLogin() {
		containerAlert(
			`<p>${i18n("login")}<a href="https://chat.openai.com" target="_blank" rel="noreferrer">chat.openai.com</a></p>`
			);
	}

	function alertBlockedByCloudflare() {
		containerAlert(
			`<p>${i18n("checkClouflare")}<a href="https://chat.openai.com" target="_blank" rel="noreferrer">chat.openai.com</a></p>`
			);
	}

	function alertFrequentRequests() {
		containerAlert(`<p>${i18n("tooManyRequests")}</p>`);
	}

	function isBlockedbyCloudflare(resp) {
		try {
			const html = new DOMParser().parseFromString(resp, "text/html");
			const title = html.querySelector("title");
			return title.innerText === "Just a moment...";
		} catch (error) {
			return false;
		}
	}
	async function getAnswer(question, callback) {
		try {
			const accessToken = await getAccessToken();
			GM_xmlhttpRequest({
				method: "POST",
				url: "https://chat.openai.com/backend-api/conversation",
				headers: {
					"Content-Type": "	application/json",
					Authorization: `Bearer ${accessToken}`
				},
				responseType: responseType(),
				data: JSON.stringify({
					action: "next",
					messages: [{
						id: uuidv4(),
						role: "user",
						content: {
							content_type: "text",
							parts: [question]
						}
					}],
					model: "text-davinci-002-render",
					parent_message_id: uuidv4()
				}),
				onloadstart: onloadstart(),
				onload: onload(),
				onerror: function(event) {
					console.error("getAnswer error: ", event);
				},
				ontimeout: function(event) {
					console.error("getAnswer timeout: ", event);
				}
			});
		} catch (error) {
			if (error === "UNAUTHORIZED") {
				removeAccessToken();
				alertLogin();
			}
			console.error("getAnswer error: ", error);
		}

		function responseType() {
			if (getUserscriptManager() === "Tampermonkey") {
				return "stream";
			} else {
				return "text";
			}
		}

		function onload() {
			function finish() {
				if (typeof callback === "function") {
					return callback("finish");
				}
			}
			finish();
			return function(event) {
				if (event.status === 401) {
					removeAccessToken();
					alertLogin();
				}
				if (event.status === 403) {
					alertBlockedByCloudflare();
				}
				if (event.status === 429) {
					alertFrequentRequests();
				}
				if (getUserscriptManager() !== "Tampermonkey") {
					if (event.response) {
						const answer = JSON.parse(event.response.split("\n\n").slice(-3, -2)[0].slice(6))
							.message.content.parts[0];
						containerShow(answer);
					}
				}
			};
		}

		function onloadstart() {
			if (getUserscriptManager() === "Tampermonkey") {
				return function(stream) {
					const reader = stream.response.getReader();
					reader.read().then(function processText({
						done,
						value
					}) {
						if (done) {
							return;
						}
						let responseItem = String.fromCharCode(...Array.from(value));
						const items = responseItem.split("\n\n");
						if (items.length > 2) {
							const lastItem = items.slice(-3, -2)[0];
							if (lastItem.startsWith("data: [DONE]")) {
								responseItem = items.slice(-4, -3)[0];
							} else {
								responseItem = lastItem;
							}
						}
						if (responseItem.startsWith("data: {")) {
							const answer = JSON.parse(responseItem.slice(6)).message.content.parts[
								0];
							containerShow(answer);
						} else if (responseItem.startsWith("data: [DONE]")) {
							return;
						}
						return reader.read().then(processText);
					});
				};
			}
		}
	}

	function removeAccessToken() {
		GM_deleteValue("accessToken");
	}

	function getAccessToken() {
		return new Promise(async (resolve, rejcet) => {
			const accessToken = await GM_getValue("accessToken");
			if (!accessToken) {
				GM_xmlhttpRequest({
					url: "https://chat.openai.com/api/auth/session",
					onload: function(response) {
						if (isBlockedbyCloudflare(response.responseText)) {
							alertLogin();
							return;
						}
						const accessToken2 = JSON.parse(response.responseText).accessToken;
						if (!accessToken2) {
							rejcet("UNAUTHORIZED");
						}
						GM_setValue("accessToken", accessToken2);
						resolve(accessToken2);
					},
					onerror: function(error) {
						rejcet(error);
					},
					ontimeout: () => {
						console.error("getAccessToken timeout!");
					}
				});
			} else {
				resolve(accessToken);
			}
		});
	}
	const _default = "";

	function getWebsite() {
		if (location.hostname.indexOf(".google.") !== -1) {
			return configRequestImmediately("google");
		}
		switch (location.hostname) {
			case "www.bing.com":
			case "cn.bing.com":
				return configRequestImmediately("bing");
			case "www.baidu.com":
				return configRequestImmediately("baidu");
			case "duckduckgo.com":
				return configRequestImmediately("duckduckgo");
			case "www.deepl.com":
				return configRequestAfterClickButton("deepl");
			default:
				throw new Error(`unknow website: ${location.hostname}`);
		}

		function configRequestImmediately(name) {
			return {
				name,
				type: "immediately"
			};
		}

		function configRequestAfterClickButton(name) {
			return {
				name,
				type: "after-click-button"
			};
		}
	}

	function getQuestion() {
		switch (getWebsite().name) {
			case "baidu":
				return new URL(window.location.href).searchParams.get("wd");
			default:
				return new URL(window.location.href).searchParams.get("q");
		}
	}

	function getPosition() {
		return GM_getValue("containerPosition", 1);
	}

	function setPosition(newPosition) {
		GM_setValue("containerPosition", newPosition);
	}

	function initUI() {
		initContainer();
		switch (getWebsite().name) {
			case "google":
				googleInjectContainer();
				break;
			case "bing":
				bingInjectContainer();
				break;
			case "baidu":
				baiduInjectContainer();
				break;
			case "duckduckgo":
				duckduckgoInjectContainer();
				break;
			case "deepl":
				deeplInjectContainer();
				break;
			default:
				alertUnknowError();
		}

		function googleInjectContainer() {
			if (getPosition() === 1) {
				const container2 = getContainer();
				const siderbarContainer = document.getElementById("rhs");
				if (siderbarContainer) {
					siderbarContainer.prepend(container2);
				} else {
					container2.classList.add("sidebar-free");
					document.getElementById("rcnt").appendChild(container2);
				}
			} else {
				GM_addStyle(".chat-gpt-container{max-width: 100%!important}");
				const container2 = getContainer();
				const mainContainer = document.querySelector("#search");
				if (mainContainer) {
					mainContainer.prepend(container2);
				}
			}
		}

		function bingInjectContainer() {
			if (getPosition() === 1) {
				const container2 = getContainer();
				const siderbarContainer = document.getElementById("b_context");
				siderbarContainer.prepend(container2);
			} else {
				GM_addStyle(".chat-gpt-container{max-width: 100%!important}");
				GM_addStyle(".chat-gpt-container{width: 70vw}");
				const container2 = getContainer();
				const mainBarContainer = document.querySelector("main");
				mainBarContainer.prepend(container2);
			}
		}

		function baiduInjectContainer() {
			if (getPosition() === 1) {
				const container2 = getContainer();
				const siderbarContainer = document.getElementById("content_right");
				siderbarContainer.prepend(container2);
			} else {
				GM_addStyle(".chat-gpt-container{max-width: 100%!important}");
				const container2 = getContainer();
				const siderbarContainer = document.querySelector("#content_left");
				siderbarContainer.prepend(container2);
			}
		}

		function duckduckgoInjectContainer() {
			const container2 = getContainer();
			const siderbarContainer = document.getElementsByClassName("results--sidebar")[0];
			siderbarContainer.prepend(container2);
		}

		function deeplInjectContainer() {
			const container2 = getContainer();
			container2.style.maxWidth = "1000px";
			const button = document.createElement("button");
			button.innerHTML = i18n("chatGPTTranslate");
			button.className = "chat-gpt-translate-button";
			document.getElementsByClassName("lmt__textarea_container")[0].appendChild(button);
			button.addEventListener("click", function() {
				initContainer();
				button.disabled = true;
				try {
					document.getElementsByClassName("lmt__raise_alternatives_placement")[0].insertBefore(
						container2, document.getElementsByClassName("lmt__translations_as_text")[0]);
				} catch {
					document.getElementsByClassName("lmt__textarea_container")[1].insertBefore(container2,
						document.getElementsByClassName("lmt__translations_as_text")[0]);
				}
				let outlang = document.querySelectorAll("strong[data-testid='deepl-ui-tooltip-target']")[0]
					.innerHTML;
				let question = "Translate the following paragraph into " + outlang + " and only " +
					outlang + "\n\n" + document.getElementById("source-dummydiv").innerHTML;
				getAnswer(question, (t) => {
					button.disabled = false;
				});
			});
		}
	}

	function initMenu() {
		let position_id = GM_registerMenuCommand(i18n("containerPosition") + getPosition(), position_switch, "M");

		function position_switch() {
			GM_unregisterMenuCommand(position_id);
			setPosition((getPosition() + 1) % 2);
			position_id = GM_registerMenuCommand(i18n("containerPosition") + getPosition(), position_switch, "M");
			location.reload();
		}
	}
	async function main() {
		initUI();
		initMenu();
		if (getWebsite().type === "immediately") {
			getAnswer(getQuestion());
		}
	}
	main().catch((e) => {
		console.error(e);
	});
})();
